using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using BouncinBall;


namespace BouncinBall.Components
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class Ball : VisibleComponent
    {
        public const string TEXTURE_FILENAME = "ball_32x32";

        public Utility.Position Position { get; set; }
        public Utility.Velocity Velocity { get; set; }
        /// <summary>
        /// Get ball diameter
        /// </summary>
        public float Diameter { get { return _dia; } }
        private float _dia;
        private SoundEffect _soundHit;
        private SoundEffectInstance _soundAirborn;

        /// <summary>
        /// Change ball velocity based on hit/bounce
        /// </summary>
        /// <param name="v">Velocity of surface being contacted</param>
        /// <param name="surfaceNormalDirection">Vector describing direction of surface normal on contact</param>
        public void Hit(Utility.Velocity v, Vector3 surfaceNormalDirection)
        {
            //Determine ball-bounce (reflective component is new velocity)
            // 1)This assumes bounce, meaning direction of ball changes by ~180
            //   not a push in the same direction the ball is currently going
            //b = a - 2 * (a . n^) * n^ where n^ is unit vector normal to paddle surface
            surfaceNormalDirection.Normalize();
            Velocity += new Utility.Velocity(-2 * Utility.Helper.DotProduct(Velocity.VelocityVector, surfaceNormalDirection), surfaceNormalDirection);
            //Velocity = new Utility.Velocity(Velocity.Magnitude, Velocity.UnitDirection + 2 * surfaceNormalDirection); //guessed approach
            //Add contacting surface velocity component to ball
            // 1) This assumes all energy is imparted to point particle, without spin taken into account
            Velocity += v;

            _soundHit.Play();
        }

        public BoundingSphere BoundingShape
        {
            get
            {
                return new BoundingSphere(Position.FullPosition, Diameter / 2);
            }
        }

        #region Constructors

        public Ball(Game game)
            : base(game, TEXTURE_FILENAME, BouncingBallGame.BALL_DEPTH)
        {
            // TODO: Construct any child components here
            Position = new Utility.Position();
            Velocity = new Utility.Velocity();
            _dia = 0;
        }

        public Ball(Game game, float diameter) : this(game)
        {
            _dia = diameter;
        }

        #endregion Constructors

        #region Overrides

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            SetSpriteScale(Diameter);

            _soundHit = Game.Content.Load<SoundEffect>("cowbell");
            SoundEffect soundAirborn = Game.Content.Load<SoundEffect>("1kHz_tone_long");
            _soundAirborn = soundAirborn.CreateInstance();
            _soundAirborn.IsLooped = true;
            //_soundAirborn.Play();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            base.Update(gameTime);

            Velocity.ApplyGravity(((BouncingBallGame)this.Game).Field.Gravity, (float)gameTime.ElapsedGameTime.TotalSeconds);

            Position.ApplyVelocity(Velocity, (float)gameTime.ElapsedGameTime.TotalSeconds);

            if (((BouncingBallGame)Game).IsPlaying)
            {
                if (_soundAirborn.State == SoundState.Stopped)
                    _soundAirborn.Play();
                _soundAirborn.Volume = Utility.Helper.GetSoundEffectVolume(Velocity.Dz, 10.0F);
                _soundAirborn.Pitch = Utility.Helper.GetSoundEffectPitch(Velocity.Dz, 10.0F);
            }
            else
                _soundAirborn.Stop();
        }

        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);
        }

        #endregion Overrides
    }
}
